/*
 * Reksio - Memory Map Editor
 * Copyright (C) 2023 CERN
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
 *
 * In applying this licence, CERN does not waive the privileges and immunities
 * granted to it by virtue of its status as an Intergovernmental Organization or
 * submit itself to any jurisdiction.
 */

#include "validatornode.h"
#include "memorynode.h"
#include "attributecontainer.h"
#include "attributecontainervalidator.h"

bool ValidatorNode::validate(const MemoryNode *node, const bool recursive)
{
    bool all_valid = true;
    all_valid = _attributes->validate(node->getAttributeContainer(), recursive) && all_valid;
    if(recursive)
    {
        for(auto& child: node->getChildren())
        {
            all_valid = child->validate(recursive) && all_valid;
        }
    }
    return all_valid;
}

bool ValidatorNode::isValid(const MemoryNode *node, const bool recursive) const
{
    bool all_valid = true;
    all_valid = _attributes->isValid(node->getAttributeContainer(), recursive) && all_valid;
    if(recursive)
    {
        for(const auto& child: node->getChildren())
        {
            all_valid = child->isValid(recursive) && all_valid;
        }
    }
    return all_valid;
}

const std::string &ValidatorNode::getName() const
{
    return _name;
}

std::vector<std::string> ValidatorNode::getRelativeName(const ValidatorNode *parent) const
{
    // go up until non-attribute sequence is there
    std::vector<std::string> full_name = {getName()};

    if(parent == this)
        return full_name;

    ValidatorNode* node = _parent;
    while(parent != node && node) // iterate until parent is not attribute sequence
    {
        full_name.insert(full_name.begin(), node->getName());
        node = node->getParent();
    }
    return full_name;
}

ValidatorNode *ValidatorNode::getParent() const
{
    return _parent;
}

PythonValidator *ValidatorNode::getPythonValidator() const
{
    return _py_validator.get();
}

bool ValidatorNode::isDeprecated() const
{
    return _deprecated;
}

void ValidatorNode::setDeprecated(bool deprecated)
{
    _deprecated = deprecated;
}

const std::string &ValidatorNode::getDeprecatedMessage() const
{
    return _deprecated_msg;
}

void ValidatorNode::setDeprecatedMessage(const std::string &msg)
{
    _deprecated_msg = msg;
}

void ValidatorNode::addPythonMenuAction(const std::string &name, const std::string& py_module, const std::string& py_function)
{
    _py_menu_actions.push_back({name, {py_module, py_function}});
}

const std::vector<std::pair<std::string, std::pair<std::string, std::string>>> &ValidatorNode::getPythonMenuActions() const
{
    return _py_menu_actions;
}

void ValidatorNode::setParent(ValidatorNode *parent)
{
    _parent = parent;
}

bool ValidatorNode::isAttributeSequence() const
{
    return _attribute_sequence;
}

void ValidatorNode::setAttributeSequence(bool val)
{
    _attribute_sequence = val;
}

bool ValidatorNode::isChildrenSequence() const
{
    return _children_sequence;
}

void ValidatorNode::setChildrenSequence(bool val)
{
    _children_sequence = val;
}

bool ValidatorNode::isSpecialSequence() const
{
    return isAttributeSequence() || isChildrenSequence();
}

bool ValidatorNode::runPythonValidator(const MemoryNode* node)
{
    if(hasPythonValidator())
        return getPythonValidator()->validate(node);
    return true;
}

bool ValidatorNode::hasPythonValidator() const
{
    return _py_validator.get() != nullptr;
}

void ValidatorNode::updateParent()
{
    for(auto& child: _children)
        child->setParent(this);
    _attributes->setParent(this);
}

ValidatorNode::ValidatorNode(const std::string& name):
    _attributes(std::make_unique<AttributeContainerValidator>("")),
    _children(),
    _name(name),
    _parent(nullptr),
    _attribute_sequence(false),
    _children_sequence(false)
{
    updateParent();
}

ValidatorNode &ValidatorNode::operator=(ValidatorNode &&rhs)
{
    _attributes.swap(rhs._attributes);
    _children.swap(rhs._children);
    _children_mem.swap(rhs._children_mem);
    _name.swap(rhs._name);
    _parent = rhs._parent;
    _attribute_sequence = rhs._attribute_sequence;
    _children_sequence = rhs._children_sequence;
    _py_validator.swap(rhs._py_validator);
    _deprecated = rhs._deprecated;
    _deprecated_msg.swap(rhs._deprecated_msg);
    _py_menu_actions.swap(rhs._py_menu_actions);

    updateParent();

    return *this;
}

ValidatorNode::ValidatorNode(ValidatorNode &&rhs)
{
    _attributes.swap(rhs._attributes);
    _children.swap(rhs._children);
    _children_mem.swap(rhs._children_mem);
    _name.swap(rhs._name);
    _parent = rhs._parent;
    _attribute_sequence = rhs._attribute_sequence;
    _children_sequence = rhs._children_sequence;
    _py_validator.swap(rhs._py_validator);
    _deprecated = rhs._deprecated;
    _deprecated_msg.swap(rhs._deprecated_msg);
    _py_menu_actions.swap(rhs._py_menu_actions);

    updateParent();
}

void ValidatorNode::addChildValidator(std::unique_ptr<ValidatorNode> validator)
{
    validator->setParent(this);
    _children.push_back(validator.get());
    _children_mem.push_back(std::move(validator));
}

ValidatorNode* ValidatorNode::addChildValidator(const std::string &name)
{
    std::unique_ptr<ValidatorNode> validator(std::make_unique<ValidatorNode>(name));
    ValidatorNode* validator_ptr = validator.get();
    addChildValidator(std::move(validator));
    return validator_ptr;
}

void ValidatorNode::addPythonValidator(std::unique_ptr<PythonValidator> validator)
{
    _py_validator.swap(validator);
}

ValidatorNode* ValidatorNode::getChild(const std::string &name) const
{
    for(const auto& child: _children)
    {
        if(child->getName() == name)
            return child;
    }
    return nullptr;
}

bool ValidatorNode::isAllowed(const std::string &type) const
{
    return getChild(type) != nullptr;
}

const std::vector<ValidatorNode*> &ValidatorNode::getChildren() const
{
    return _children;
}

std::vector<ValidatorNode* > &ValidatorNode::getChildren()
{
    return _children;
}

std::vector<std::unique_ptr<ValidatorNode> > &ValidatorNode::getChildrenMem()
{
    return _children_mem;
}

std::vector<ValidatorNode*> ValidatorNode::getChildrenWithSpecialSequences() const
{
    std::vector<ValidatorNode*> result;
    for(const auto& child: _children)
    {
        if(child->isSpecialSequence())
        {
            auto child_res = child->getChildrenWithSpecialSequences();
            result.insert(result.end(), child_res.begin(), child_res.end());
        }
        else
            result.push_back(child);
    }
    return result;
}

const AttributeContainerValidator* ValidatorNode::getAttributes() const
{
    return _attributes.get();
}

AttributeContainerValidator* ValidatorNode::getAttributes()
{
    return _attributes.get();
}
